---
title: Vercel AI SDK RSC Runtime
---

## Overview

Integration with the Vercel AI SDK React Server Components. It allows streaming React components directly from the server.  
Integrates with OpenAI, Anthropic, Mistral, Perplexity, AWS Bedrock, Azure, Google Gemini, Hugging Face, Fireworks, Cohere, LangChain, Replicate, Ollama, and more.

## Example

[RSC Example App](https://assistant-ui-rsc-example.vercel.app/)

## Getting Started

import { Steps, Step } from "fumadocs-ui/components/steps";
import { Callout } from "fumadocs-ui/components/callout";

<Steps>
  <Step>
  ### Create a Next.JS project

```sh
npx create-next-app@latest my-app
cd my-app
```

  </Step>
  <Step>

### Install Vercel AI SDK and `@assistant-ui/react-ai-sdk`

```sh npm2yarn
npm install @assistant-ui/react @assistant-ui/react-ai-sdk ai @ai-sdk/openai zod nanoid
```

  </Step>
  <Step>

### Setup `actions.tsx`

`@/app/actions.tsx`

```tsx
"use server";

import { createAI, getMutableAIState, streamUI } from "ai/rsc";
import { openai } from "@ai-sdk/openai";
import { ReactNode } from "react";
import { z } from "zod";
import { nanoid } from "nanoid";

export interface ServerMessage {
  role: "user" | "assistant";
  content: string;
}

export interface ClientMessage {
  id: string;
  role: "user" | "assistant";
  display: ReactNode;
}

export async function continueConversation(
  input: string,
): Promise<ClientMessage> {
  "use server";

  const history = getMutableAIState();

  const result = await streamUI({
    model: openai("gpt-3.5-turbo"),
    messages: [...history.get(), { role: "user", content: input }],
    text: ({ content, done }) => {
      if (done) {
        history.done((messages: ServerMessage[]) => [
          ...messages,
          { role: "assistant", content },
        ]);
      }

      return <div>{content}</div>;
    },
    tools: {
      deploy: {
        description: "Deploy repository to vercel",
        parameters: z.object({
          repositoryName: z
            .string()
            .describe("The name of the repository, example: vercel/ai-chatbot"),
        }),
        generate: async function* ({ repositoryName }) {
          yield <div>Cloning repository {repositoryName}...</div>; // [!code highlight:5]
          await new Promise((resolve) => setTimeout(resolve, 3000));
          yield <div>Building repository {repositoryName}...</div>;
          await new Promise((resolve) => setTimeout(resolve, 2000));
          return <div>{repositoryName} deployed!</div>;
        },
      },
    },
  });

  return {
    id: nanoid(),
    role: "assistant",
    display: result.value,
  };
}

export const AI = createAI<ServerMessage[], ClientMessage[]>({
  actions: {
    continueConversation,
  },
  initialAIState: [],
  initialUIState: [],
});
```

  </Step>
  <Step>

### Define a `MyRuntimeProvider` component

`@/app/MyRuntimeProvider.tsx`

```tsx
"use client";

import {
  type AppendMessage,
  AssistantRuntimeProvider,
} from "@assistant-ui/react";
import { useVercelRSCRuntime } from "@assistant-ui/react-ai-sdk";
import { useActions, useUIState } from "ai/rsc";
import { nanoid } from "nanoid";

import type { AI } from "./actions";

export function MyRuntimeProvider({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const { continueConversation } = useActions();
  const [messages, setMessages] = useUIState<typeof AI>();

  const onNew = async (m: AppendMessage) => {
    if (m.content[0]?.type !== "text")
      throw new Error("Only text messages are supported");

    const input = m.content[0].text;
    setMessages((currentConversation) => [
      ...currentConversation,
      { id: nanoid(), role: "user", display: input },
    ]);

    const message = await continueConversation(input);

    setMessages((currentConversation) => [...currentConversation, message]);
  };

  const runtime = useVercelRSCRuntime({ messages, onNew });

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      {children}
    </AssistantRuntimeProvider>
  );
}
```

  </Step>
  <Step>

### Wrap your app in `AI` and `MyRuntimeProvider`

`@/app/layout.tsx`

```tsx {1-2,12-13,19-20}
import { AI } from '@/app/actions';
import { MyRuntimeProvider } from '@/app/MyRuntimeProvider';

...

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <AI>
      <MyRuntimeProvider>
        <html lang="en">
          <body className={inter.className}>
            {children}
          </body>
        </html>
      </MyRuntimeProvider>
    </AI>
  )
}
```

  </Step>
</Steps>

## Accessing AI SDK Messages

You can use the `getExternalStoreMessages` utility to convert `ThreadMessage`s back to your message format.

```tsx
const MyAssistantMessage = () => {
  const myMessage = useMessage((m) => getExternalStoreMessages(m)[0]);
  // ...
};
```
